package com.zkh.myutils.database.data.tools;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Date;
import com.zkh.myutils.database.DatabaseException;

/**
 * 类型适配器
 * @author zkh
 */
public class TypeAdapter {
	/**
	 * 特殊方法之oracle.sql.CLOB.stringValue
	 */
	private static Method oracleClobStringValue;
	/**
	 * ORACLE之CLOB类型
	 */
	private final static byte TYPE_ORACLE_CLOB = 0;
	/**
	 * 特殊方法之oracle.sql.TIMESTAMP.timestampValue
	 */
	private static Method oracleTimestampTimestampValue;
	/**
	 * ORACLE之TIMESTAMP类型
	 */
	private final static byte TYPE_ORACLE_TIMESTAMP = 1;
	
	/**
	 * 类型修正
	 * @param value 获取到的值
	 * @param jdbcType jdbc类型，通过java.sql.Types对比
	 * @param javaType 待转换的目标类型Class
	 */
	public static Object typeFix(Object value, int jdbcType, Class<?> javaType) {
		//值不为空
		if(value!=null) {
			try {
				//类名
				String className = javaType.getName();
				//Oracle数据库Number默认会转为BigDecimal
				if(value instanceof BigDecimal){
					value = convertType(value, javaType);
				}//数据库日期类型转java.sql.Timestamp(sqlserver和Oracle日期类型须使用java.sql.Timestamp保存)
				else if(value instanceof Date && "java.sql.Timestamp".equals(className)){
					value = new Timestamp(((Date) value).getTime());
				}//oracle的TIMESTAMP类型转java.sql.Timestamp类型
				else if(jdbcType==Types.TIMESTAMP && "oracle.sql.TIMESTAMP".equals(value.getClass().getName())){
					value = value.getClass().getMethod("timestampValue").invoke(value);
				}//oracle的CLOB类型
				else if(jdbcType==Types.CLOB){
					//使用oracle.sql.CLOB类的stringValue方法读取CLOB类型的值（ojdbc7+）
					value = value.getClass().getMethod("stringValue").invoke(value);
				}
			}catch(Exception e) {
				throw new DatabaseException("类型转换发生异常", e);
			}
		}
		return value;
	}
	
	/**
	 * 为Map结果集修正类型（某些类型取值不方便）
	 * @param value 获取到的值
	 * @param jdbcType jdbc类型，通过java.sql.Types对比
	 * @param javaType 待转换的目标类型Class
	 */
	public static Object typeFixForMap(Object value, int jdbcType) {
		//值不为空
		if(value!=null) {
			try {
				//oracle的TIMESTAMP类型转java.sql.Timestamp类型
				if(jdbcType==Types.TIMESTAMP && "oracle.sql.TIMESTAMP".equals(value.getClass().getName())){
					value = getMethod(value, TYPE_ORACLE_TIMESTAMP).invoke(value);
				}//oracle的CLOB类型
				else if(jdbcType==Types.CLOB){
					//使用oracle.sql.CLOB类的stringValue方法读取CLOB类型的值（ojdbc7+）
					value = getMethod(value, TYPE_ORACLE_CLOB).invoke(value);
				}
			}catch(Exception e) {
				throw new DatabaseException("类型转换发生异常", e);
			}
		}
		return value;
	}
	
	/**
	 * 获取特殊方法。如果有缓存，就取缓存
	 * @param value 当前值，用来获取Class
	 * @param type 方法类型
	 */
	private static Method getMethod(Object value, byte type) throws NoSuchMethodException, SecurityException {
		//ORABLE_CLOB类型（ojdbc7+）
		if(type==TYPE_ORACLE_CLOB) {
			//类型为空，创建类型，否则直接返回
			return oracleClobStringValue!=null ? oracleClobStringValue :
				(oracleClobStringValue = value.getClass().getMethod("stringValue"));
		}
		//ORABLE_TIMESTAMP类型
		if(type==TYPE_ORACLE_TIMESTAMP) {
			//类型为空，创建类型，否则直接返回
			return oracleTimestampTimestampValue!=null ? oracleTimestampTimestampValue :
				(oracleTimestampTimestampValue = value.getClass().getMethod("timestampValue"));
		}
		//其它，默认返回null
		return null;
	}
	
	/**
	 * BigDecimal转对应类型
	 * @param val 待转换的BigDecimal类型数据
	 * @param cls 要转换的类型的class属性
	 */
	public static Object convertType(Object val, Class<?> cls) {
		//值为空
		if(val==null) {
			return null;
		}
		//保存类型
		String e = cls.getName();
		//根据类型执行不同处理方法
		switch (e) {
			//字符串
			case "java.lang.String":
				return val instanceof String ? val : val.toString();
			//目标类型为日期，默认原类型也是日期或其子类
			case "java.util.Date":
				return val;
			default: {
				//值如果是Number
				if(val instanceof Number) {
					//类型转换
					Number v = (Number) val;
					//分别处理
					switch (e) {
						case "java.lang.Boolean":
						case "boolean":
							return v.byteValue()==1;
						case "java.lang.Long":
						case "long":
							return v.longValue();
						case "java.lang.Integer":
						case "int":
							return v.intValue();
						case "java.lang.Short":
						case "short":
							return v.shortValue();
						case "java.lang.Byte":
						case "byte":
							return v.byteValue();
						case "java.lang.Float":
						case "float":
							return v.floatValue();
						case "java.lang.Double":
						case "double":
							return v.doubleValue();
						//BigDecimal和BigInteger直接和返回类型相同，不支持转换
						case "java.math.BigDecimal":
						case "java.math.BigInteger":
							return val;
					}
				}//字符串
				else if(val instanceof String && "java.lang.Boolean".equals(e)) {
					return "true".equals(val);
				}
				throw new DatabaseException("无法转换的类型：" + val + "-->" + e);
			}
		}
	}
}
